//
//  LBPageMenu.m
//  LBPageMenu
//
//  Created by Apple on 2021/9/21.
//

#import "LBPageMenu.h"

static NSInteger LBPageMenuButtonTagBase = 100;

@interface LBPageMenu ()

//标题数组
@property (nonatomic, strong) NSArray *titles;
//底部跟踪指示条
@property (nonatomic, strong) UIImageView *tracker;
// 指示条高度
@property (nonatomic, assign) CGFloat trackerHeight;
// button 容器
@property (nonatomic, strong) UIScrollView *itemScrollView;
//button 数组
@property (nonatomic, strong) NSMutableArray <UIButton *> *buttons;
//当前选中给的button
@property (nonatomic, strong) UIButton *selectedButton;
//当前选中的button 索引
@property (nonatomic, assign) NSInteger selectedItemIndex;

// 起始偏移量,为了判断滑动方向
@property (nonatomic, assign) CGFloat beginOffsetX;
//顶部细线
@property (nonatomic, strong) UIView *topLine;

//// 配置
@property (nonatomic, strong) LBPageMenuConfiguration *configuration;

///配置属性
///内边距, 默认 为 UIEdgeInsetMake(0, 10, 0 , 10);
@property (nonatomic, assign) UIEdgeInsets contentInsets;

///跟踪器的高度, 默认 4
@property (nonatomic, assign) CGFloat trackHeight;

///未选中标题文案颜色 默认灰色
@property (nonatomic, strong) UIColor *unSelectedTitleColor;
///选中的标题颜色 默认黑色
@property (nonatomic, strong) UIColor *selectedTittleColor;
/// 字体 默认14
@property (nonatomic, strong) UIFont *font;
///选中标题字体
@property (nonatomic, strong) UIFont *selectedFont;
/// 跟踪器颜色, 默认黑色
@property (nonatomic, strong) UIColor *trackLineColor;
/// 指示器风格
@property (nonatomic, assign) LBPageMenuIndicatorStyle indicatorStyle;
///指定指示器宽度，在LBPageMenuIndicatorStyleStretchDesignateddLength 样式下起作用
@property (nonatomic, assign) CGFloat designatedIndicatorLength;

@end

@implementation LBPageMenu

- (instancetype)initWithFrame:(CGRect)frame
                configuration:(nonnull LBPageMenuConfiguration *)configuration{
    if (self = [super initWithFrame:frame]) {
        [self initialConfiguration:configuration];
        [self setUpUI];
    }
    return self;
}

- (void)initialConfiguration:(LBPageMenuConfiguration *)configuration
{
    self.contentInsets = configuration.contentInsets;
    self.trackHeight = configuration.trackHeight;
    self.trackLineColor = configuration.trackLineColor;
    self.font = configuration.font;
    self.selectedFont = configuration.selectedFont;
    self.unSelectedTitleColor = configuration.unSelectedTitleColor;
    self.selectedTittleColor = configuration.selectedTittleColor;
    self.indicatorStyle = configuration.indicatorStyle;
    self.designatedIndicatorLength = configuration.designatedIndicatorLength;
    self.configuration = configuration;
}

- (void)setUpUI {
    [self addSubview:self.itemScrollView];
}

- (void)setItems:(NSArray *)items selectedItemIndex:(NSUInteger)selectedItemIndex {
    self.titles = items.copy;
    self.selectedItemIndex = selectedItemIndex;
    [self setUpButtons];
    if (self.buttons.count) {
        // 默认选中selectedItemIndex对应的按钮
        UIButton *selectedButton = [self.buttons objectAtIndex:selectedItemIndex];
        [self buttonInPageMenuClicked:selectedButton];
        [self.itemScrollView insertSubview:self.tracker atIndex:0];
        [self resetSetupTrackerFrameWithSelectedButton:selectedButton];
    }
}

// 按钮点击方法
- (void)buttonInPageMenuClicked:(UIButton *)sender {
    self.selectedButton.titleLabel.font = self.font;
    sender.titleLabel.font = self.selectedFont;
    
    CGFloat fromIndex = self.selectedButton ? self.selectedButton.tag- LBPageMenuButtonTagBase : sender.tag - LBPageMenuButtonTagBase;
    CGFloat toIndex = sender.tag - LBPageMenuButtonTagBase;
    // 更新下item对应的下标,必须在代理之前，否则外界在代理方法中拿到的不是最新的,必须用下划线，用self.会造成死循环
    _selectedItemIndex = toIndex;
    
    if (fromIndex != toIndex) { // 如果相等，说明是第一次进来，或者2次点了同一个，此时不需要动画
        [self moveTrackerWithSelectedButton:sender];
    }
    [self handleSelecedSender:sender];
}


- (void)setUpButtons
{
    CGFloat width = 0;
    for (int i = 0; i < self.titles.count; i++)
    {
        NSString *title = self.titles[i];
#ifdef DEBUG
        NSAssert([title isKindOfClass:[NSString class]], @"items中的元素只能是NSString");
#endif
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setTitle:title forState:UIControlStateNormal];
        [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
        button.tag = LBPageMenuButtonTagBase + i;
        [button setTitleColor:self.unSelectedTitleColor forState:UIControlStateNormal];
        button.titleLabel.font = self.font;
        [button setTitleColor:self.selectedTittleColor forState:UIControlStateSelected];
        CGFloat textW = [title boundingRectWithSize:CGSizeMake(MAXFLOAT, CGRectGetHeight(self.bounds)) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.font} context:nil].size.width + 10;
        button.frame = CGRectMake(width, 0, textW, CGRectGetHeight(self.bounds) - self.trackHeight);
        width += textW;
        if (i == self.titles.count - 1) {
            self.itemScrollView.contentSize = CGSizeMake(CGRectGetMaxX(button.frame), 0);
        }
        
        [self.buttons addObject:button];
        [self.itemScrollView addSubview:button];
    }
}

#pragma mark - 跟踪相关

- (void)beginMoveTrackerFollowScrollView:(UIScrollView *)scrollView {
    // 这个if条件的意思就是没有滑动的意思
    if (!scrollView.dragging && !scrollView.decelerating) {
        return;
    }
    // 当滑到边界时，继续通过scrollView的bouces效果滑动时，直接return
    if (scrollView.contentOffset.x < 0 || scrollView.contentOffset.x > scrollView.contentSize.width-scrollView.bounds.size.width) {
        return;
    }
    static int i = 0;
    if (i == 0) {
        // 记录起始偏移量，注意千万不能每次都记录，只需要第一次纪录即可。
        // 初始值不能等于scrollView.contentOffset.x,因为第一次进入此方法时，scrollView.contentOffset.x已经有偏移并非刚开始的偏移
        _beginOffsetX = scrollView.bounds.size.width * self.selectedItemIndex;
        i = 1;
    }
    // 当前偏移量
    CGFloat currentOffSetX = scrollView.contentOffset.x;
    // 偏移进度
    CGFloat offsetProgress = currentOffSetX / scrollView.bounds.size.width;
    
    CGFloat progress = offsetProgress - floor(offsetProgress);
    
    
    NSInteger fromIndex;
    NSInteger toIndex;
    // 以下注释的“拖拽”一词很准确，不可说成滑动，例如:当手指向右拖拽，还未拖到一半时就松开手，接下来scrollView则会往回滑动，这个往回，就是向左滑动，这也是_beginOffsetX不可时刻纪录的原因，如果时刻纪录，那么往回(向左)滑动时会被视为“向左拖拽”,然而，这个往回却是由“向右拖拽”而导致的
    if (currentOffSetX - _beginOffsetX > 0) { // 向左拖拽了
        // 求商,获取上一个item的下标
        fromIndex = currentOffSetX / scrollView.bounds.size.width;
        // 当前item的下标等于上一个item的下标加1
        toIndex = fromIndex + 1;
        if (toIndex >= self.buttons.count) {
            toIndex = fromIndex;
        }
    } else if (currentOffSetX - _beginOffsetX < 0) {  // 向右拖拽了
        toIndex = currentOffSetX / scrollView.bounds.size.width;
        fromIndex = toIndex + 1;
        progress = 1.0 - progress;
    } else {
        progress = 1.0;
        fromIndex = self.selectedItemIndex;
        toIndex = fromIndex;
    }
    if (currentOffSetX == scrollView.bounds.size.width * fromIndex) {// 滚动停止了
        progress = 1.0;
        toIndex = fromIndex;
    }
    // 如果滚动停止，直接通过点击按钮选中toIndex对应的item
    if (currentOffSetX == scrollView.bounds.size.width*toIndex) { // 这里toIndex==fromIndex
        i = 0;
        // 这一次赋值起到2个作用，一是点击toIndex对应的按钮，走一遍代理方法,二是弥补跟踪器的结束跟踪，因为本方法是在scrollViewDidScroll中调用，可能离滚动结束还有一丁点的距离，本方法就不调了,最终导致外界还要在scrollView滚动结束的方法里self.selectedItemIndex进行赋值,直接在这里赋值可以让外界不用做此操作
        if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
            toIndex = self.buttons.count - 1 - toIndex;
        }
        self.selectedItemIndex = toIndex;
        UIButton *button = [self.buttons objectAtIndex:toIndex];
        [self handleSelecedSender:button];
        // 要return，点击了按钮，跟踪器自然会跟着被点击的按钮走
        return;
    }
 //    没有关闭跟踪模式
    if (self.indicatorStyle == LBPageMenuIndicatorStyleDefault) {
        if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
            toIndex = self.buttons.count - 1 - toIndex;
            fromIndex = self.buttons.count - 1 - fromIndex;
        }
        [self moveTrackerWithProgress:progress fromIndex:fromIndex toIndex:toIndex currentOffsetX:currentOffSetX beginOffsetX:_beginOffsetX];
    } else if (self.indicatorStyle == LBPageMenuIndicatorStyleStretch ||
               self.indicatorStyle == LBPageMenuIndicatorStyleStretchDesignateddLength) {
        [self changeTrackerFrameWithOffsetProgress:offsetProgress];
    } else if (self.indicatorStyle == LBPageMenuIndicatorStyleLineChange) {
        [self lineChangeIndicatorWhenScroll:scrollView];
    }
    
}

- (void)moveTrackerWithProgress:(CGFloat)progress fromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex currentOffsetX:(CGFloat)currentOffsetX beginOffsetX:(CGFloat)beginOffsetX {
    // 下面才开始真正滑动跟踪器，上面都是做铺垫
    UIButton *fromButton = self.buttons[fromIndex];
    UIButton *toButton = self.buttons[toIndex];
    
    // 2个按钮之间的距离
    CGFloat xDistance = toButton.center.x - fromButton.center.x;
    // 2个按钮宽度的差值
    CGFloat wDistance = toButton.frame.size.width - fromButton.frame.size.width;
    
    CGRect newFrame = self.tracker.frame;
    CGPoint newCenter = self.tracker.center;
    
    newCenter.x = fromButton.center.x + xDistance * progress;
    newFrame.size.width = fromButton.frame.size.width + wDistance * progress;
    self.tracker.frame = newFrame;
    self.tracker.center = newCenter;
}

// 移动跟踪器
- (void)moveTrackerWithSelectedButton:(UIButton *)selectedButton {
    [UIView animateWithDuration:0.25 animations:^{
        [self resetSetupTrackerFrameWithSelectedButton:selectedButton];
    }];
}

///指示条拉伸样式
- (void)changeTrackerFrameWithOffsetProgress:(CGFloat)offsetProgress
{
    NSInteger baseIndex = floorf(offsetProgress);
    if (baseIndex > self.buttons.count - 1 && baseIndex < 0) {
        return;
    }
    CGFloat progressStrech = offsetProgress - baseIndex;
    CGRect leftFrame = self.buttons[baseIndex].frame;
    CGRect rightFrame = self.buttons[baseIndex + 1].frame;
 
    CGFloat leftWidth = CGRectGetWidth(leftFrame);
    CGFloat rightWidth = CGRectGetWidth(rightFrame);
    CGFloat leftX = CGRectGetMinX(leftFrame);
    CGFloat rightX = CGRectGetMinX(rightFrame);
    ///最大宽度就是两个按钮的宽度之和加上间隙
    CGFloat maxWidth = rightX - leftX + CGRectGetWidth(rightFrame);
    if (self.indicatorStyle == LBPageMenuIndicatorStyleStretchDesignateddLength) {
        /// 指定宽度样式
        leftX = leftX + (leftWidth - self.designatedIndicatorLength) / 2;
        rightX = rightX + (rightWidth - self.designatedIndicatorLength) / 2;
        leftWidth = self.designatedIndicatorLength;
        rightWidth = self.designatedIndicatorLength;
        maxWidth = rightX - leftX + self.designatedIndicatorLength;
    }
    CGFloat targetX;
    CGFloat targetWidth;
    //前50%，只增加width；后50%，移动x并减小width
    if (progressStrech <= 0.5) {
        targetX = leftX;
        targetWidth = [self interpolationFrom:leftWidth to:maxWidth percent:progressStrech * 2];
    } else {
        targetX = [self interpolationFrom:leftX to:rightX percent:(progressStrech - 0.5)*2];
        targetWidth = [self interpolationFrom:maxWidth to:rightWidth percent:(progressStrech - 0.5)*2];
    }
    CGRect frame = self.tracker.frame;
    frame.origin.x = targetX;
    frame.size.width = targetWidth;
    self.tracker.frame = frame;
}

- (void)lineChangeIndicatorWhenScroll:(UIScrollView *)scrollView
{
    CGFloat curPage = scrollView.contentOffset.x / scrollView.bounds.size.width;
    // 左边label角标
    NSInteger leftIndex = curPage;
    // 右边的label角标
    NSInteger rightIndex = leftIndex + 1;
    if(self.buttons.count <= leftIndex) {
        return;
    }
    // 获取右边的button
    UIButton *rightButton;
    if (rightIndex < self.buttons.count ) {
        rightButton = self.buttons[rightIndex];
    }
    // 计算下右边缩放比例
    CGFloat rightScale = curPage - leftIndex;
    // 计算下左边缩放比例
    CGFloat leftScale = 1 - rightScale;
    
    // 计算偏移量
    CGFloat offsetX = 0 ;
    if(self.buttons.count >= 1) {
        offsetX = offsetX + CGRectGetWidth(self.buttons[0].frame) * 0.5;
    }
    
    NSInteger indexInt = ((NSInteger)(scrollView.contentOffset.x/ CGRectGetWidth(scrollView.frame)));
    CGFloat indexFloat = scrollView.contentOffset.x / CGRectGetWidth(scrollView.frame) - indexInt;
    for(NSInteger i = 0; i<indexInt; i++) {
        offsetX = offsetX + CGRectGetWidth(self.buttons[i].frame) /2 + ( self.buttons.count > (i+1) ?  CGRectGetWidth(self.buttons[i + 1].frame)/2 : 0);
    }
    if(self.buttons.count > indexInt ) {
        offsetX = offsetX + (CGRectGetWidth(self.buttons[indexInt].frame)/2  +(self.buttons.count > (indexInt+1) ? CGRectGetWidth(self.buttons[indexInt + 1].frame)/2 : 0)) * indexFloat;
    }
    CGRect frame = self.tracker.frame;
    CGSize size = frame.size;
    if(rightIndex <  self.buttons.count) {
        size.width = CGRectGetWidth(self.buttons[leftIndex].frame) * leftScale + CGRectGetWidth(self.buttons[rightIndex].frame)  * rightScale;
        frame.size = size;
    } else {
        size.width = CGRectGetWidth(self.buttons[leftIndex].frame) * leftScale;
        frame.size = size;
    }
    self.tracker.frame = frame;
    CGPoint center = self.tracker.center;
    center.x = offsetX;
    self.tracker.center = center;
    offsetX = offsetX - CGRectGetWidth(self.frame) * 0.5;
    if (offsetX < 0) offsetX = 0;
    // 获取最大滚动范围
    CGFloat maxOffsetX = self.itemScrollView.contentSize.width - CGRectGetWidth(self.itemScrollView.frame);
    if (offsetX > maxOffsetX) offsetX = maxOffsetX;
    // 滚动标题滚动条
    [self.itemScrollView setContentOffset:CGPointMake(offsetX, 0) animated:NO];
}

- (void)resetSetupTrackerFrameWithSelectedButton:(UIButton *)selectedButton {
    CGFloat trackerX;
    CGFloat trackerY;
    CGFloat trackerW;
    CGFloat trackerH;
    
    CGFloat selectedButtonWidth = selectedButton.frame.size.width;
    
    trackerW = selectedButtonWidth;
    if (self.indicatorStyle == LBPageMenuIndicatorStyleStretchDesignateddLength) {
        trackerW = self.designatedIndicatorLength;
    }
    trackerH = self.trackHeight;
    trackerX = selectedButton.frame.origin.x;
    trackerY = self.itemScrollView.bounds.size.height - trackerH;
    self.tracker.frame = CGRectMake(trackerX, trackerY, trackerW, trackerH);
    
    CGPoint trackerCenter = self.tracker.center;
    trackerCenter.x = selectedButton.center.x;
    self.tracker.center = trackerCenter;
}

#pragma mark - 滚动 标题scrollview
// 点击button让itemScrollView发生偏移
- (void)moveItemScrollViewWithSelectedButton:(UIButton *)selectedButton {
    if (CGRectEqualToRect(self.itemScrollView.frame, CGRectZero)) {
        return;
    }
    CGPoint centerInPageMenu = selectedButton.center;
    // CGRectGetMidX(self.backgroundView.frame)指的是屏幕水平中心位置，它的值是固定不变的
    CGFloat offSetX = centerInPageMenu.x - CGRectGetMidX(self.itemScrollView.frame);
    
    // itemScrollView的容量宽与自身宽之差(难点)
    CGFloat maxOffsetX = self.itemScrollView.contentSize.width - self.itemScrollView.frame.size.width;
    // 如果选中的button中心x值小于或者等于itemScrollView的中心x值，或者itemScrollView的容量宽度小于itemScrollView本身，此时点击button时不发生任何偏移，置offSetX为0
    if (offSetX <= 0 || maxOffsetX <= 0) {
        offSetX = 0;
    }
    // 如果offSetX大于maxOffsetX,说明itemScrollView已经滑到尽头，此时button也发生任何偏移了
    else if (offSetX > maxOffsetX){
        offSetX = maxOffsetX;
    }

    [self.itemScrollView setContentOffset:CGPointMake(offSetX, 0) animated:YES];
}

#pragma mark - action
- (void)buttonClick:(UIButton *)sender
{
    CGFloat selectIndex = sender.tag - LBPageMenuButtonTagBase;
    if (self.delegate && [self.delegate respondsToSelector:@selector(selectItemAtIndex:)]) {
        [self.delegate selectItemAtIndex:selectIndex];
    }
    [self buttonInPageMenuClicked:sender];
}

#pragma mark  - util
- (void)handleSelecedSender:(UIButton *)sender
{
    [self.selectedButton setTitleColor:self.unSelectedTitleColor forState:UIControlStateNormal];
    [sender setTitleColor:self.selectedTittleColor forState:UIControlStateNormal];
    self.selectedButton = sender;
    [self moveItemScrollViewWithSelectedButton:sender];
}

#pragma mark - util
- (CGFloat)interpolationFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent
{
    percent = MAX(0, MIN(1, percent));
    return from + (to - from)*percent;
}

#pragma mark - lazy load
- (UIScrollView *)itemScrollView
{
    if (!_itemScrollView) {
        _itemScrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
        _itemScrollView.contentInset = self.configuration.contentInsets;
    }
    return _itemScrollView;
}

- (UIImageView *)tracker {
    
    if (!_tracker) {
        _tracker = [[UIImageView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxX(self.bounds) - self.trackHeight, 0, self.trackHeight)];
        _tracker.layer.cornerRadius = _trackerHeight * 0.5;
        _tracker.layer.masksToBounds = YES;
        _tracker.backgroundColor = self.trackLineColor;
    }
    return _tracker;
}

- (NSMutableArray<UIButton *> *)buttons
{
    if (!_buttons) {
        _buttons = [NSMutableArray array];
    }
    return _buttons;
}

@end
